﻿using DataWorks_Tools.BasicMethods;
using DataWorks_Tools.MappModals;
using DataWorks_Tools.MergeDetails;
using MathNet.Numerics.LinearAlgebra;
using Microsoft.ML.OnnxRuntime;
using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DataWorks_Tools.LSTMDetails
{
    public static class LSTMPredictProcess
    {
        /// <summary>
        /// 多线程版本(只模型多线程)。不再写死 WFT / For
        /// 通过模型名自动识别 类别 key（比如含有 WFT、For、ABC 等）
        /// 每个类别的模型共用一个 XScaler，每个模型依然有独立的 YScaler
        /// </summary>
        /// <param name="row"></param>
        /// <returns></returns>
        public static async Task<List<string>> YXHExcuteLSTMPredictMultiGroupXScalerAutoAsync(DataLSTMPredictModal row)
        {
            // 存储执行日志或提示信息
            List<string> list = new List<string>();
            // 确保结果保存路径存在
            Directory.CreateDirectory(row.rdpath);
            // 获取待预测的日期文件夹集合（即原始路径→结果路径的对应关系）
            var ds = BasicFilesOperation.YXHGetProcessedDateDir(row.odpath, row.rdpath, row.datedir);
            // 若没有找到任何待处理的文件夹，则直接返回
            if (ds == null || ds.Length == 0)
                return list;

            Stopwatch timer = new Stopwatch();
            timer.Start();
            // 获取所有 onnx 模型文件
            var onnxfilelst = BasicFilesOperation.YXHGetAllextFiles(row.onnxfilepath, "*.onnx");
            // 获取所有归一化 scaler 文件（通常为 xScaler 与 yScaler）
            var scalerfilelst = BasicFilesOperation.YXHGetAllextFiles(row.scalerpath, "*.csv");
            // 如果没有找到任何模型文件，直接返回提示
            if (onnxfilelst == null || onnxfilelst.Length == 0)
            {
                list.Add("未找到任何 onnx 模型文件");
                return list;
            }

            // ✅ 缓存不同类别的 XScaler
            var xScalerCache = new Dictionary<string, MinMaxScaler>(StringComparer.OrdinalIgnoreCase);

            // ✅ 模型缓存
            var modelCache = new Dictionary<string, (InferenceSession session, MinMaxScaler xScaler, MinMaxScaler yScaler)>();
            // 遍历所有 onnx 模型文件，逐个加载
            foreach (var onnxfile in onnxfilelst)
            {
                // 去掉后缀，提取模型名（如 xxx_model.onnx → xxx）
                var modelName = onnxfile.Name.Replace("_model.onnx", "");

                // 获取该模型对应的 XScaler 和 YScaler 文件路径
                var scalerPair = OnnxTools.SelectScaler(onnxfile, scalerfilelst); // [xScalerFile, yScalerFile]
                // 若缺失 scaler 文件则跳过该模型
                if (scalerPair == null || scalerPair.Length < 2)
                {
                    list.Add($"模型 {modelName} 缺少对应的 Scaler 文件，跳过");
                    continue;
                }

                // ✅ 自动识别类别 ✅ 自动识别该模型所属类别（如根据名称中包含的关键字区分组别）
                string groupKey = DetectGroupKey(modelName);

                // ✅ 如果该类 XScaler 尚未缓存，就加载一次
                if (!xScalerCache.TryGetValue(groupKey, out var xScaler))
                {
                    xScaler = new MinMaxScaler(scalerPair[0].FullName);
                    xScalerCache[groupKey] = xScaler;
                }
                // 创建 ONNX 推理会话
                var session = new InferenceSession(onnxfile.FullName);
                // 加载该模型的 YScaler（反归一化用）
                var yScaler = new MinMaxScaler(scalerPair[1].FullName);
                // 缓存模型及对应的 scaler 实例
                modelCache[modelName] = (session, xScaler, yScaler);
            }
            // 若最终无任何可用模型，直接返回提示
            if (modelCache.Count == 0)
            {
                list.Add("未能成功加载任何模型及其Scaler");
                return list;
            }

            foreach( var d in ds)
            {
                try
                {
                    // 获取当前文件夹下的分类文件列表（每组包含一批CSV/TXT）
                    var classifiedlist = MergeDetailsMethods.YXHGetClassifyFileListperFolder(d, mergetype.txt, 1, 1);
                    if (classifiedlist == null || classifiedlist.Count == 0) continue;
                    // 创建该日期对应的输出结果文件夹
                    string resultdirectorypath = Path.Combine(row.rdpath, d.Name);
                    Directory.CreateDirectory(resultdirectorypath);
                    // 将当前目录下的 txt 文件复制到结果目录
                    var txtinfo = BasicFilesOperation.YXHGetAllextFiles(d.FullName, "*.txt");
                    foreach (var file in txtinfo)
                    {
                        var txtresultpath = Path.Combine(resultdirectorypath, file.Name);
                        file.CopyTo(txtresultpath, true);
                    }
                    // 存储每个模型对应的 lastN
                    var lastNDict = new ConcurrentDictionary<string, List<float[]>>();
                    foreach ( var part in classifiedlist)
                    {
                        // 当前组内的 CSV 文件列表
                        var fileinfo = part.filelist;
                        // 所有模型名
                        var modelNames = modelCache.Keys.ToList();
                        //var lastN = new List<float[]>();
                        var newheaders = new List<string>();
                        //循环每一个文件
                        for (int i = 0; i < fileinfo.Count; i++)
                        {
                            string resultpath = Path.Combine(resultdirectorypath, fileinfo[i].Name);
                            var csv = new CsvNormalizer(fileinfo[i]);
                            // 使用无状态方法获取归一化数据
                            var normInputData = new Dictionary<string, List<float[]>>();
                            var wftresults = new List<List<float>>();
                            if (csv.isFirst)
                                newheaders.AddRange(csv.header);
                            var resultsDict = new ConcurrentDictionary<string, List<float>>();
                            // 最大并发数为4
                            var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = 4 };
                            Parallel.ForEach(modelNames, parallelOptions, modelName => {
                                var (session, xScaler, yScaler) = modelCache[modelName];
                                // 每个模型独立计算 finalInput
                                var modelNormInput = csv.GetNormalizedInput(xScaler);
                                List<float[]> finalInput;
                                List<float[]> modelLastN;
                                if (csv.isFirst)
                                {
                                    var zeros = OnnxTools.CreateZeroPadding(modelNormInput);
                                    finalInput = CsvNormalizer.MergeFloatArrayLists(zeros, modelNormInput, false);
                                    //第一个文件需要获取最后50行数据赋给lastN
                                    modelLastN = csv.GetLastNElements(modelNormInput);

                                }
                                else
                                {
                                    // 使用该模型之前保存的 lastN
                                    modelLastN = lastNDict.GetValueOrDefault(modelName) ?? csv.GetLastNElements(modelNormInput);
                                    finalInput = CsvNormalizer.MergeFloatArrayLists(modelLastN, modelNormInput, true);
                                    //从第二个文件开始，先用前一个的lastN然后再覆盖
                                    modelLastN = csv.GetLastNElements(modelNormInput);
                                }
                                // 更新该模型的 lastN
                                lastNDict[modelName] = modelLastN;
                                var inputTensors = OnnxTools.CreateLstmInputTensors(finalInput);
                                var predictions = OnnxTools.PredictBatch(session, inputTensors);
                                var scalerpre = OnnxTools.FlattenToList(predictions);
                                var results = csv.GetDenormalizedValues(scalerpre, yScaler, modelName);
                                resultsDict[modelName] = results;
                            });
                            await Task.CompletedTask; // 为了确保方法返回的 Task 不会被忽略
                            // 保证顺序：按 modelNames 添加到 wftresults
                            foreach (var modelName in modelNames)
                            {
                                wftresults.Add(resultsDict[modelName]);
                                // ✅ 只在第一次 CSV 文件时，把模型名加到 newheaders
                                if (csv.isFirst && !newheaders.Contains(modelName))
                                    newheaders.Add(modelName);
                            }

                            csv.onlyData.AddRange(wftresults);
                            await OnnxTools.SaveCSVfromArrayData(csv.onlyData, newheaders, resultpath);
                        }
                    }
                }
                catch (Exception ex) 
                {
                    list.Add(ex.Message);
                }
             
            }


            timer.Stop();
            list.Add($"预测程序总完成时间为{timer.Elapsed.TotalMinutes:0.00}分 - 并发版本 (线程数限制为4, 自动分组XScaler缓存)");

            // ✅ 最后释放模型资源
            foreach (var model in modelCache.Values)
            {
                model.session.Dispose();
            }

            return list;
        }



        public static async Task<List<string>> YXHExcuteLSTMPredictMultiGroupXScalerAutoGPUAsync(DataLSTMPredictModal row)
        {
            // 存储执行日志或提示信息
            List<string> list = new List<string>();
            // 确保结果保存路径存在
            Directory.CreateDirectory(row.rdpath);
            // 获取待预测的日期文件夹集合（即原始路径→结果路径的对应关系）
            var ds = BasicFilesOperation.YXHGetProcessedDateDir(row.odpath, row.rdpath, row.datedir);
            // 若没有找到任何待处理的文件夹，则直接返回
            if (ds == null || ds.Length == 0)
                return list;

            Stopwatch timer = new Stopwatch();
            timer.Start();
            // 获取所有 onnx 模型文件
            var onnxfilelst = BasicFilesOperation.YXHGetAllextFiles(row.onnxfilepath, "*.onnx");
            // 获取所有归一化 scaler 文件（通常为 xScaler 与 yScaler）
            var scalerfilelst = BasicFilesOperation.YXHGetAllextFiles(row.scalerpath, "*.csv");
            // 如果没有找到任何模型文件，直接返回提示
            if (onnxfilelst == null || onnxfilelst.Length == 0)
            {
                list.Add("未找到任何 onnx 模型文件");
                return list;
            }

            // ✅ 缓存不同类别的 XScaler
            var xScalerCache = new Dictionary<string, MinMaxScaler>(StringComparer.OrdinalIgnoreCase);

            // ✅ 模型缓存
            var modelCache = new Dictionary<string, (InferenceSession session, MinMaxScaler xScaler, MinMaxScaler yScaler)>();
            // 遍历所有 onnx 模型文件，逐个加载
            foreach (var onnxfile in onnxfilelst)
            {
                // 去掉后缀，提取模型名（如 xxx_model.onnx → xxx）
                var modelName = onnxfile.Name.Replace("_model.onnx", "");

                // 获取该模型对应的 XScaler 和 YScaler 文件路径
                var scalerPair = OnnxTools.SelectScaler(onnxfile, scalerfilelst); // [xScalerFile, yScalerFile]
                // 若缺失 scaler 文件则跳过该模型
                if (scalerPair == null || scalerPair.Length < 2)
                {
                    list.Add($"模型 {modelName} 缺少对应的 Scaler 文件，跳过");
                    continue;
                }

                // ✅ 自动识别类别 ✅ 自动识别该模型所属类别（如根据名称中包含的关键字区分组别）
                string groupKey = DetectGroupKey(modelName);

                // ✅ 如果该类 XScaler 尚未缓存，就加载一次
                if (!xScalerCache.TryGetValue(groupKey, out var xScaler))
                {
                    xScaler = new MinMaxScaler(scalerPair[0].FullName);
                    xScalerCache[groupKey] = xScaler;
                }
                var sessionOptions = new SessionOptions();
                sessionOptions.AppendExecutionProvider_CUDA(0); // 0 是你的 GPU ID

                // 创建 ONNX 推理会话
                var session = new InferenceSession(onnxfile.FullName, sessionOptions);
                // 加载该模型的 YScaler（反归一化用）
                var yScaler = new MinMaxScaler(scalerPair[1].FullName);
                // 缓存模型及对应的 scaler 实例
                modelCache[modelName] = (session, xScaler, yScaler);
            }
            // 若最终无任何可用模型，直接返回提示
            if (modelCache.Count == 0)
            {
                list.Add("未能成功加载任何模型及其Scaler");
                return list;
            }

            foreach (var d in ds)
            {
                try
                {
                    // 获取当前文件夹下的分类文件列表（每组包含一批CSV/TXT）
                    var classifiedlist = MergeDetailsMethods.YXHGetClassifyFileListperFolder(d, mergetype.txt, 1, 1);
                    if (classifiedlist == null || classifiedlist.Count == 0) continue;
                    // 创建该日期对应的输出结果文件夹
                    string resultdirectorypath = Path.Combine(row.rdpath, d.Name);
                    Directory.CreateDirectory(resultdirectorypath);
                    // 将当前目录下的 txt 文件复制到结果目录
                    var txtinfo = BasicFilesOperation.YXHGetAllextFiles(d.FullName, "*.txt");
                    foreach (var file in txtinfo)
                    {
                        var txtresultpath = Path.Combine(resultdirectorypath, file.Name);
                        file.CopyTo(txtresultpath, true);
                    }
                    // 存储每个模型对应的 lastN
                    var lastNDict = new ConcurrentDictionary<string, List<float[]>>();
                    foreach (var part in classifiedlist)
                    {
                        // 当前组内的 CSV 文件列表
                        var fileinfo = part.filelist;
                        // 所有模型名
                        var modelNames = modelCache.Keys.ToList();
                        //var lastN = new List<float[]>();
                        var newheaders = new List<string>();
                        //循环每一个文件
                        for (int i = 0; i < fileinfo.Count; i++)
                        {
                            string resultpath = Path.Combine(resultdirectorypath, fileinfo[i].Name);
                            var csv = new CsvNormalizer(fileinfo[i]);
                            // 使用无状态方法获取归一化数据
                            var normInputData = new Dictionary<string, List<float[]>>();
                            var wftresults = new List<List<float>>();
                            if (csv.isFirst)
                                newheaders.AddRange(csv.header);
                            var resultsDict = new ConcurrentDictionary<string, List<float>>();
                            // 最大并发数为4
                            var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = 4 };
                            Parallel.ForEach(modelNames, parallelOptions, modelName => {
                                var (session, xScaler, yScaler) = modelCache[modelName];
                                // 每个模型独立计算 finalInput
                                var modelNormInput = csv.GetNormalizedInput(xScaler);
                                List<float[]> finalInput;
                                List<float[]> modelLastN;
                                if (csv.isFirst)
                                {
                                    var zeros = OnnxTools.CreateZeroPadding(modelNormInput);
                                    finalInput = CsvNormalizer.MergeFloatArrayLists(zeros, modelNormInput, false);
                                    //第一个文件需要获取最后50行数据赋给lastN
                                    modelLastN = csv.GetLastNElements(modelNormInput);

                                }
                                else
                                {
                                    // 使用该模型之前保存的 lastN
                                    modelLastN = lastNDict.GetValueOrDefault(modelName) ?? csv.GetLastNElements(modelNormInput);
                                    finalInput = CsvNormalizer.MergeFloatArrayLists(modelLastN, modelNormInput, true);
                                    //从第二个文件开始，先用前一个的lastN然后再覆盖
                                    modelLastN = csv.GetLastNElements(modelNormInput);
                                }
                                // 更新该模型的 lastN
                                lastNDict[modelName] = modelLastN;
                                var inputTensors = OnnxTools.CreateLstmInputTensors(finalInput);
                                var predictions = OnnxTools.PredictBatch(session, inputTensors);
                                var scalerpre = OnnxTools.FlattenToList(predictions);
                                var results = csv.GetDenormalizedValues(scalerpre, yScaler, modelName);
                                resultsDict[modelName] = results;
                            });
                            await Task.CompletedTask; // 为了确保方法返回的 Task 不会被忽略
                            // 保证顺序：按 modelNames 添加到 wftresults
                            foreach (var modelName in modelNames)
                            {
                                wftresults.Add(resultsDict[modelName]);
                                // ✅ 只在第一次 CSV 文件时，把模型名加到 newheaders
                                if (csv.isFirst && !newheaders.Contains(modelName))
                                    newheaders.Add(modelName);
                            }

                            csv.onlyData.AddRange(wftresults);
                            await OnnxTools.SaveCSVfromArrayData(csv.onlyData, newheaders, resultpath);
                        }
                    }
                }
                catch (Exception ex)
                {
                    list.Add(ex.Message);
                }

            }


            timer.Stop();
            list.Add($"预测程序总完成时间为{timer.Elapsed.TotalMinutes:0.00}分 - 并发版本 (线程数限制为4, 自动分组XScaler缓存)");

            // ✅ 最后释放模型资源
            foreach (var model in modelCache.Values)
            {
                model.session.Dispose();
            }

            return list;
        }

        /// <summary>
        /// 自动识别模型类别的工具方法
        /// </summary>
        private static string DetectGroupKey(string modelName)
        {
            // 可以根据实际业务扩展
            if (modelName.Contains("WFT", StringComparison.OrdinalIgnoreCase)) return "WFT";
            if (modelName.Contains("For", StringComparison.OrdinalIgnoreCase)) return "For";
            // 默认分组：Other
            return "Other";
        }


    }
}
